﻿#region MIT
/*
Engine Of Evermore (http://code.google.com/p/engineofevermore/)
Copyright (c) 2008 Michael Woerister

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

using WeifenLuo.WinFormsUI.Docking;
using Blue.ResourceManager;
using EngineOfEvermore.Imaging;
using EngineOfEvermore.Common;
using EngineOfEvermore.Editor.Common;

namespace EngineOfEvermore.Editor.ImageMapEditor
{
    public partial class ImageMapEditor : Form, IStatusMessageConsumer
    {
        #region Variables
        ImageMapEditorConfig _config;
        
        ImageMapExplorer _imageMapExplorer = new ImageMapExplorer();
        ImageExplorer _imageExplorer = new ImageExplorer();
        ImageMapProperties _imageMapProperties = new ImageMapProperties();

        List<ImageMapInstance> _imageMapInstances = new List<ImageMapInstance>();
        ImageMapInstance _activeInstance;

        public static readonly HashSet<String> ValidImageExtension = new HashSet<String>( 
            new String[] { ".png", ".bmp", ".jpeg", ".jpg" } 
            );

        #endregion

        internal ImageMapInstance ActiveInstance
        {
            get { return _activeInstance; }
        }

        #region Constructor
        public ImageMapEditor()
        {
            InitializeComponent();

            _imageMapExplorer.ImageMapEditor = this;
            _imageMapProperties.ImageMapEditor = this;
            _imageExplorer.ImageMapEditor = this;
        }
        #endregion

        #region Comm with SubEditors
        /// <summary>
        /// Called by any newly created ImageMapInstance.
        /// </summary>
        internal void OnImageMapInstanceCreated( ImageMapInstance instance )
        {
            Debug.Assert( !_imageMapInstances.Contains( instance ) );

            _imageMapInstances.Add( instance );
            instance.Enter += _onInstanceEnterFocus;
            instance.FormClosing += _onInstanceClosing;
        }

        /// <summary>
        /// Called by ImageMapProperties when a property of the current FrameGenerator
        /// was changed. Routes to change to the current ImageMapInstance.
        /// </summary>
        internal void OnFrameGeneratorUpdated( PropertyValueChangedEventArgs e )
        {
            Debug.Assert( _imageMapProperties.ImageMapInstance == _activeInstance );
            
            _activeInstance.OnFrameGeneratorUpdated( e );

            _updateButtons();
        }

        /// <summary>
        /// Called by ImageMapProperties when another FrameGenerator is selected.
        /// Sets the new Generator in the active ImageMapInstance.
        /// </summary>
        internal void OnFrameGeneratorChanged( ImageMap.IFrameGenerator newGenerator )
        {
            Debug.Assert( _imageMapProperties.ImageMapInstance == _activeInstance );

            _activeInstance.ActiveFrameGenerator = newGenerator;
            _imageMapProperties.ImageMapInstance = _activeInstance;

            _updateButtons();
        }

        /// <summary>
        /// Called by ImageMapExplorer when opening a *.ximp file. This method
        /// creates a new ImageMapInstance Form and shows it or - if the *.ximp file
        /// is already open - puts focus to the existing ImageMapInstance form.
        /// </summary>
        /// <param directoryName="node">The TreeNode representing the *.ximp file.</param>
        internal void OnOpenImageMapFromResource( String resourceName )
        {
            Debug.Assert( 
                _imageMapInstances.Count( x => x.ReferencedImageMap == resourceName ) <= 1 );

            ImageMapInstance inst =
                _imageMapInstances.Find( x => x.ReferencedImageMap == resourceName );

            if ( inst != null )
            {
                DockWindow documentDockWin = _dockPanel.DockWindows[DockState.Document];
                DockPane pane = documentDockWin.NestedPanes.Single( x => x.Contains( inst ) );

                pane.ActiveContent = inst;
                inst.Focus();
            }
            else
            {
                bool succeeded = true;

                try
                {
                    inst = ImageMapInstance.CreateFromImageMapResource( resourceName, this );
                }
                catch ( Exception )
                {
                    succeeded = false;
                }

                if ( succeeded )
                {
                    inst.Show( _dockPanel );
                    _statusMessageLabel.Text = "Successfully loaded ImageMap " + 
                        resourceName + " ...";
                }
            }
        }


        /// <summary>
        /// Gets or Sets the current message in the status bar.
        /// </summary>
        public String StatusMessage
        {
            get { return _statusMessageLabel.Text; }
            set { _statusMessageLabel.Text = value; }
        }

        /// <summary>
        /// The ImageMapEditorConfig used by this editor.
        /// </summary>
        internal ImageMapEditorConfig Config
        {
            get { return _config; }
        }

        internal bool ShowFrameNumbers
        {
            get { return _showFrameNumbersMenuItem.Checked; }
        }
        #endregion

        #region Event callbacks
        private void _onInstanceEnterFocus( Object sender, EventArgs e )
        {
            Debug.Assert( sender is ImageMapInstance );

            ImageMapInstance instance = (ImageMapInstance) sender;

            if ( instance != _activeInstance )
            {
                _activeInstance = instance;

                // Tell the image map property form
                _imageMapProperties.ImageMapInstance = instance;

                _updateButtons();
            }
        }

        private void _onInstanceClosing( Object sender, FormClosingEventArgs e )
        {
            Debug.Assert( sender is ImageMapInstance );
            Debug.Assert( _imageMapInstances.Contains( (ImageMapInstance) sender ) );

            ImageMapInstance instance = (ImageMapInstance) sender;

            if ( instance.HasUnsavedChanges )
            {
                DialogResult result =
                    MessageBox.Show( "This document contains unsaved changes that will be lost if it is closed. Do you want to continue?",
                    "Unsaved Changes", 
                    MessageBoxButtons.OKCancel, 
                    MessageBoxIcon.Warning, 
                    MessageBoxDefaultButton.Button2 
                    );

                if ( result == DialogResult.Cancel )
                {
                    e.Cancel = true;
                    return;
                }
            }

            
            instance.FormClosing -= _onInstanceClosing;
            instance.Enter -= _onInstanceEnterFocus;
            
            _imageMapInstances.Remove( instance );

            if ( _activeInstance == instance )
            {
                _activeInstance = null;
                _imageMapProperties.ImageMapInstance = null;
            }

            _updateButtons();
        }

        private void _imageMapEditor_Load( object sender, EventArgs e )
        {
            try
            {
                _config = ConfigFile.Load<ImageMapEditorConfig>( ImageMapEditorConfig.FILENAME );
            }
            catch
            {
                _config = new ImageMapEditorConfig();
            }

            if ( _config.FormPosition != new Point( -1, -1 ) )
            {
                Location = _config.FormPosition;
            }

            if ( !_config.FormSize.IsEmpty )
            {
                Size = _config.FormSize;
            }

            if ( _config.IsMaximized )
            {
                WindowState = FormWindowState.Maximized;
            }
            else
            {
                WindowState = FormWindowState.Normal;
            }

            _showFrameNumbersMenuItem.Checked = _config.ShowFrameNumbers;

            if ( File.Exists( _config.LayoutFilePath ) )
            {
                _dockPanel.LoadFromXml( _config.LayoutFilePath, _loadLayout );
            }
            else
            {
                _imageMapExplorer.Show( _dockPanel );
                _imageExplorer.Show( _dockPanel );
                _imageMapProperties.Show( _dockPanel );
            }

            ResourceManager.ResourceRenamed += new EventHandler<ResourceRenamedEventArgs>( ResourceManager_ResourceRenamed );
        }

        public void OnTryRenameResource( TryRenameResourceEventArgs e )
        {
            if ( !e.IsDirectory )
            {
                foreach ( ImageMapInstance inst in _imageMapInstances )
                {
                    if ( e.OldResourceName == inst.ReferencedImageMap && inst.HasUnsavedChanges )
                    {
                        DialogResult result =
                            MessageBox.Show( "The ImageMap " + inst.ReferencedImageMap +
                                " has unsaved changes. Do you want to save this changes before moving the ImageMap?",
                                "Save changes?",
                                MessageBoxButtons.OKCancel,
                                MessageBoxIcon.Question,
                                MessageBoxDefaultButton.Button2
                                );

                        if ( result == DialogResult.OK )
                        {
                            inst.SaveImageMapAsResource( inst.ReferencedImageMap );
                        }
                        else
                        {
                            e.Cancel = true;
                            return;
                        }
                    }
                }
            }
            else
            {
                foreach ( ImageMapInstance inst in _imageMapInstances )
                {
                    if ( inst.ReferencedImageMap.StartsWith( e.OldResourceName )  
                         && inst.HasUnsavedChanges )
                    {
                        DialogResult result =
                            MessageBox.Show( "The ImageMap " + inst.ReferencedImageMap +
                                " has unsaved changes. Do you want to save this changes before moving the ImageMap?",
                                "Save changes?",
                                MessageBoxButtons.OKCancel,
                                MessageBoxIcon.Question,
                                MessageBoxDefaultButton.Button2
                                );

                        if ( result == DialogResult.OK )
                        {
                            inst.SaveImageMapAsResource( inst.ReferencedImageMap );
                        }
                        else
                        {
                            e.Cancel = true;
                            return;
                        }
                    }
                }
            }
        }

        void ResourceManager_ResourceRenamed( object sender, ResourceRenamedEventArgs e )
        {
            Debug.Assert( Path.GetExtension( e.NewName ) == Path.GetExtension( e.OldName ) );

            String extension = Path.GetExtension( e.NewName ).ToLower();

            if ( ValidImageExtension.Contains( extension ) )
            {
                foreach ( ImageMapInstance inst in _imageMapInstances )
                {
                    if ( inst.ImageResourceName == e.OldName )
                    {
                        inst.OnImageResourceNameChanged( e.NewName );
                    }
                }

                _imageMapProperties.RefreshAll();
            }
            else if ( extension == ".ximp" )
            {
                foreach ( ImageMapInstance inst in _imageMapInstances )
                {
                    if ( inst.ReferencedImageMap == e.OldName )
                    {
                        inst.ReferencedImageMap = e.NewName;
                    }
                }
            }

            _updateButtons();
        }

        private IDockContent _loadLayout( String persistString )
        {
            if ( persistString == typeof( ImageMapExplorer ).ToString() )
            {
                return _imageMapExplorer;
            }

            if ( persistString == typeof( ImageMapProperties ).ToString() )
            {
                return _imageMapProperties;
            }

            if ( persistString == typeof( ImageExplorer ).ToString() )
            {
                return _imageExplorer;
            }

            return null;
        }

        private void _imageMapEditor_FormClosing( object sender, FormClosingEventArgs e )
        {
            if ( _imageMapInstances.Any( x => x.HasUnsavedChanges ) )
            {
                DialogResult result =
                    MessageBox.Show( "There are open documents that contain unsaved changes that will be lost if the editor is closed. Do you want to continue?",
                    "Unsaved Changes",
                    MessageBoxButtons.OKCancel,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2
                    );

                if ( result == DialogResult.Cancel )
                {
                    e.Cancel = true;
                    return;
                }
            }

            _dockPanel.SaveAsXml( _config.LayoutFilePath );

            _config.IsMaximized = ( WindowState == FormWindowState.Maximized );

            _config.Save();
        }

        private void _toolStripMenuItem2_Click( object sender, EventArgs e )
        {
            Close();
        }

        private void _imageMapExplorerToolStripMenuItem_Click( object sender, EventArgs e )
        {
            _imageMapExplorer.Show( _dockPanel );
        }

        private void _imageExplorerToolStripMenuItem_Click( object sender, EventArgs e )
        {
            _imageExplorer.Show( _dockPanel );
        }
        
        private void _imageMapPropertiesToolStripMenuItem_Click( object sender, EventArgs e )
        {
            _imageMapProperties.Show( _dockPanel );
        }

        private void _imageMapEditor_Resize( object sender, EventArgs e )
        {
            if ( _config != null && this.WindowState == FormWindowState.Normal )
            {
                _config.FormSize = Size;
            }
        }

        private void _imageMapEditor_LocationChanged( object sender, EventArgs e )
        {
            if ( _config != null && this.WindowState == FormWindowState.Normal )
            {
                _config.FormPosition = Location;
            }
        }

        private void _toolStripUndoButton_Click( object sender, EventArgs e )
        {
            _undo();
        }

        private void _toolStripRedoButton_Click( object sender, EventArgs e )
        {
            _redo();
        }

        void _undo()
        {
            if ( _activeInstance.CanUndo )
            {
                _activeInstance.Undo();
            }

            _updateButtons();
            _imageMapProperties.RefreshPropertyGrid();
        }

        void _redo()
        {
            if ( _activeInstance.CanRedo )
            {
                _activeInstance.Redo();
            }

            _updateButtons();
            _imageMapProperties.RefreshPropertyGrid();
        }

        void _updateButtons()
        {
            if ( _activeInstance != null )
            {
                _toolStripUndoButton.Enabled = _activeInstance.CanUndo;
                _toolStripRedoButton.Enabled = _activeInstance.CanRedo;

                _menuItemUndoButton.Enabled = _activeInstance.CanUndo;
                _menuItemRedoButton.Enabled = _activeInstance.CanRedo;

                if ( _activeInstance.HasUnsavedChanges && !_activeInstance.NeverSaved )
                {
                    _saveActiveImageMainMenu.Enabled = true;
                    _toolStripSaveButton.Enabled = true;

                    _saveActiveImageMainMenu.ToolTipText = "Save";
                    _toolStripSaveButton.Text = "Save";
                }
                else
                {
                    _saveActiveImageMainMenu.Enabled = false;
                    _toolStripSaveButton.Enabled = false;

                    if ( _activeInstance.NeverSaved )
                    {
                        _saveActiveImageMainMenu.ToolTipText =
                            "Use ImageMap Explorer ContextMenu to save an ImageMap the first time";
                        _toolStripSaveButton.Text =
                            "Use ImageMap Explorer ContextMenu to save an ImageMap the first time";
                    }
                    else
                    {
                        _saveActiveImageMainMenu.ToolTipText = "Save";
                        _toolStripSaveButton.Text = "Save";
                    }
                }
            }
            else
            {
                _toolStripUndoButton.Enabled = false;
                _toolStripRedoButton.Enabled = false;

                _menuItemUndoButton.Enabled = false;
                _menuItemRedoButton.Enabled = false;

                _saveActiveImageMainMenu.Enabled = false;
                _toolStripSaveButton.Enabled = false;
            }
        }

        private void _menuItemUndoButton_Click( object sender, EventArgs e )
        {
            _undo();
        }

        private void _menuItemRedoButton_Click( object sender, EventArgs e )
        {
            _redo();
        }
        #endregion

        private void _saveActiveImageMainMenu_Click( object sender, EventArgs e )
        {
            _saveActiveImageMap();
        }

        private void _saveActiveImageMap()
        {
            Debug.Assert( _activeInstance != null );
            Debug.Assert( _activeInstance.HasUnsavedChanges );
            Debug.Assert( !_activeInstance.NeverSaved );

            _activeInstance.SaveImageMapAsResource( _activeInstance.ReferencedImageMap );

            _updateButtons();
        }

        private void _toolStripSaveButton_Click( object sender, EventArgs e )
        {
            _saveActiveImageMap();
        }

        private void _changeFrameOutlineColor_Click( object sender, EventArgs e )
        {
            ColorDialog colorDialog = new ColorDialog();
            colorDialog.SolidColorOnly = true;
            colorDialog.AllowFullOpen = true;
            colorDialog.FullOpen = true;
            colorDialog.Color = _config.FrameOutlineColorRt;

            DialogResult result = colorDialog.ShowDialog();

            if ( result == DialogResult.OK )
            {
                _config.FrameOutlineColorRt = colorDialog.Color;

                foreach ( var inst in _imageMapInstances )
                {
                    inst.OnFrameOutlineColorChanged();
                }
            }

            colorDialog.Dispose();
        }

        private void showFrameNumbersToolStripMenuItem_Click( object sender, EventArgs e )
        {
            _showFrameNumbersMenuItem.Checked = !_showFrameNumbersMenuItem.Checked;
            _config.ShowFrameNumbers = _showFrameNumbersMenuItem.Checked;

            foreach ( ImageMapInstance inst in _imageMapInstances )
            {
                inst.OnShowFrameNumberChanged();
            }
        }

        private void _changeFrameNumberColorMenuItem_Click( object sender, EventArgs e )
        {
            ColorDialog colorDialog = new ColorDialog();
            colorDialog.SolidColorOnly = true;
            colorDialog.AllowFullOpen = true;
            colorDialog.FullOpen = true;
            colorDialog.Color = _config.FrameNumberColorRt;

            DialogResult result = colorDialog.ShowDialog();

            if ( result == DialogResult.OK )
            {
                _config.FrameNumberColorRt = colorDialog.Color;

                foreach ( var inst in _imageMapInstances )
                {
                    inst.OnFrameNumberColorChanged();
                }
            }

            colorDialog.Dispose();
        }
    }
}
